package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component  //将给类交给Spring容器管理  !!!
@Aspect     //标识该类是一个切面
@Order(2)   //按照顺序 第二个执行
public class SpringAOP {
    /**
     * 知识回顾: AOP利用动态代理扩展目标方法.
     * 公式:    切面 = 切入点表达式 + 通知方法
     * 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
     * 通知方法:  对目标方法进行扩展的封装方法.
     * 目标对象的bean的ID: userServiceImpl
     * 切入点表达式:
     *      1. bean("bean的ID")
     * AOP规则:  如果目标对象满足切入点表达式,则执行通知方法
     */
    //@Pointcut("bean(userServiceImpl)")
    //@Pointcut("within(com.*.service..*)")
    //@Pointcut("execution(* com.jt.service.UserServiceImpl.addUser())")
    @Pointcut("@annotation(com.jt.anno.Lyj)")
    public void pointcut(){

    }

    //1.前置通知: 在目标方法执行之前执行.
    //@Before("pointcut()")
    public void before(JoinPoint joinPoint){//连接点:获取方法中的数据
        Class targetClass = joinPoint.getTarget().getClass();
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        Object[] objs = joinPoint.getArgs();
        System.out.println("我是前置通知!!!!");
        System.out.println("类型:"+ targetClass);
        System.out.println("方法名称:"+methodName);
        System.out.println("类的名称:"+className);
        System.out.println("方法中携带的参数:"+ Arrays.toString(objs));
    }

    //2.后置通知: 在目标方法执行之后执行
    // 通过returning = "result"属性,获取目标方法的返回值,当作参数传递给result
    //@AfterReturning(value = "pointcut()",returning = "result")
    public void afterReturn(Object result){
        System.out.println("我是后置通知!!!!");
        System.out.println("用户的返回值为:"+result);
    }

    //3.异常通知: 目标方法执行报错时,执行该通知
    //@AfterThrowing(value = "pointcut()",throwing = "exception")
    public void afterThrowing(Exception exception){
        System.out.println("我是异常通知!!!!");
        System.out.println("获取异常信息:"+exception.getMessage());
        exception.printStackTrace();
    }

    //4.最终通知: 目标方法之后都要执行的通知
    //@After("pointcut()")
    public void after(){
        System.out.println("最终通知都要执行");
    }

    //5.重点掌握 环绕通知: 在目标方法执行前后都要执行. 控制目标方法
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知A执行前!!!!");
        //底层调用动态代理的invoke方法,执行目标方法
        Object result = joinPoint.proceed();
        System.out.println("环绕通知A执行后!!!!");
        return result;
    }
}
